package com.zlict.commonelasticsearch.service;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.Time;
import co.elastic.clients.elasticsearch.ilm.*;
import co.elastic.clients.elasticsearch.indices.*;
import co.elastic.clients.json.JsonData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

/**
 * Elasticsearch ILM（索引生命周期管理）服务 - 现代 Java API
 * 使用类型安全的 Elasticsearch 8.13.4 Java API
 * 
 * @author zlict
 */
@Service
@Slf4j
public class ElasticsearchILMService {
    
    @Autowired
    private ElasticsearchClient client;
    
    
    /**
     * 创建ILM策略 - 使用现代 Java API
     * 
     * 生命周期阶段：
     * 1. 热阶段 (hot): 0-7天，高频写入，优先级100
     * 2. 温阶段 (warm): 7天-用户自定义保留月份，只读，优先级50，无副本
     * 3. 冷阶段 (cold): 超过用户自定义保留月份后，冻结索引，优先级0，永久保留
     * 
     * @param policyName 策略名称
     * @param retentionMonths 保留月数
     * @return 是否创建成功
     */
    public boolean createILMPolicy(String policyName, int retentionMonths) {
        try {
            // 计算温阶段结束的天数（用户自定义保留月份）
            int coldStartDays = retentionMonths * 30;
            
            // 使用类型安全的构建器创建ILM策略
            PutLifecycleRequest request = PutLifecycleRequest.of(p -> p
                .name(policyName)
                .policy(policy -> policy
                    .phases(phases -> phases
                        // 热阶段配置
                        .hot(Phase.of(hot -> hot
                            .actions(JsonData.of(Map.of(
                                "rollover", Map.of(
                                    "max_age", "1d",
                                    "max_size", "50gb"
                                ),
                                "set_priority", Map.of("priority", 100)
                            )))
                        ))
                        // 温阶段配置
                        .warm(Phase.of(warm -> warm
                            .minAge(Time.of(t -> t.time("7d")))
                            .actions(JsonData.of(Map.of(
                                "set_priority", Map.of("priority", 50),
                                "allocate", Map.of("number_of_replicas", 0)
                            )))
                        ))
                        // 冷阶段配置
                        .cold(Phase.of(cold -> cold
                            .minAge(Time.of(t -> t.time(coldStartDays + "d")))
                            .actions(JsonData.of(Map.of(
                                "set_priority", Map.of("priority", 0),
                                "freeze", Map.of()
                            )))
                        ))
                    )
                )
            );
            
            // 执行请求
            PutLifecycleResponse response = client.ilm().putLifecycle(request);
            
            log.info("ILM策略 '{}' 创建成功，保留期：{} 个月", policyName, retentionMonths);
            return response.acknowledged();
            
        } catch (Exception e) {
            log.error("创建ILM策略失败: {}", e.getMessage(), e);
            return false;
        }
    }
    

    
    /**
     * 创建索引模板并关联ILM策略 - 使用现代 Java API
     * 
     * @param templateName 模板名称
     * @param indexPattern 索引模式
     * @param policyName 策略名称
     * @return 是否创建成功
     */
    public boolean createIndexTemplate(String templateName, String indexPattern, String policyName) {
        try {
            // 使用类型安全的构建器创建索引模板
            Map<String, JsonData> templateSettings = Map.of(
                "index.lifecycle.name", JsonData.of(policyName),
                "index.lifecycle.rollover_alias", JsonData.of("audit-logs")
            );
            
            PutIndexTemplateRequest request = PutIndexTemplateRequest.of(t -> t
                .name(templateName)
                .indexPatterns(indexPattern)
                .template(template -> template
                    .settings(IndexSettings.of(s -> s
                        .otherSettings(templateSettings)
                    ))
                )
                .priority(500)
            );
            
            // 执行请求
            PutIndexTemplateResponse response = client.indices().putIndexTemplate(request);
            
            log.info("索引模板 '{}' 创建成功，关联策略：{}", templateName, policyName);
            return response.acknowledged();
            
        } catch (Exception e) {
            log.error("创建索引模板失败: {}", e.getMessage(), e);
            return false;
        }
    }
    

    
    /**
     * 更新ILM策略 - 使用现代 Java API
     * 
     * @param policyName 策略名称
     * @param retentionMonths 新的保留月数
     * @return 是否更新成功
     */
    public boolean updateILMPolicy(String policyName, int retentionMonths) {
        // 更新策略实际上就是重新创建策略
        return createILMPolicy(policyName, retentionMonths);
    }
    
    /**
     * 删除ILM策略 - 使用现代 Java API
     * 
     * @param policyName 策略名称
     * @return 是否删除成功
     */
    public boolean deleteILMPolicy(String policyName) {
        try {
            DeleteLifecycleRequest request = DeleteLifecycleRequest.of(d -> d
                .name(policyName)
            );
            
            DeleteLifecycleResponse response = client.ilm().deleteLifecycle(request);
            
            log.info("ILM策略 '{}' 删除成功", policyName);
            return response.acknowledged();
            
        } catch (Exception e) {
            // 策略不存在时也认为删除成功
            if (e.getMessage().contains("not found")) {
                log.info("ILM策略 '{}' 不存在，无需删除", policyName);
                return true;
            }
            log.error("删除ILM策略失败: {}", e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * 创建初始写入别名索引 - 使用现代 Java API
     * 
     * @param aliasName 别名名称
     * @return 是否创建成功
     */
    public boolean createInitialIndexWithAlias(String aliasName) {
        try {
            // 首先检查别名是否已经存在
            if (isAliasExists(aliasName)) {
                log.info("别名 {} 已经存在，无需创建初始索引", aliasName);
                return true;
            }
            
            // 检查是否存在同名的索引或数据流
            if (isIndexOrDataStreamExists(aliasName)) {
                log.warn("检测到与别名同名的索引或数据流存在: {}", aliasName);
                return false;
            }
            
            // 生成初始索引名称
            String initialIndexName = aliasName + "-" + new SimpleDateFormat("yyyy.MM.dd").format(new Date()) + "-000001";
            
            // 使用现代 API 创建索引
            CreateIndexRequest request = CreateIndexRequest.of(c -> c
                .index(initialIndexName)
                .aliases(aliasName, a -> a.isWriteIndex(true))
            );
            
            CreateIndexResponse response = client.indices().create(request);
            
            log.info("初始索引 '{}' 创建成功，别名：{}", initialIndexName, aliasName);
            return response.acknowledged();
            
        } catch (Exception e) {
            log.error("创建初始索引失败: {}", e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * 检查别名是否存在 - 使用现代 Java API
     * 
     * @param aliasName 别名名称
     * @return 是否存在
     */
    private boolean isAliasExists(String aliasName) {
        try {
            ExistsAliasRequest request = ExistsAliasRequest.of(e -> e.name(aliasName));
            return client.indices().existsAlias(request).value();
        } catch (Exception e) {
            log.debug("检查别名失败: {}", e.getMessage());
            return false;
        }
    }
    
    /**
     * 检查索引或数据流是否存在 - 使用现代 Java API
     * 
     * @param name 索引或数据流名称
     * @return 是否存在
     */
    private boolean isIndexOrDataStreamExists(String name) {
        try {
            // 检查索引是否存在
            ExistsRequest indexRequest = ExistsRequest.of(e -> e.index(name));
            if (client.indices().exists(indexRequest).value()) {
                return true;
            }
            
            // 检查数据流是否存在
            co.elastic.clients.elasticsearch.indices.ExistsRequest dataStreamRequest = 
                co.elastic.clients.elasticsearch.indices.ExistsRequest.of(e -> e.index(name));
            return client.indices().exists(dataStreamRequest).value();
            
        } catch (Exception e) {
            log.debug("检查索引或数据流失败: {}", e.getMessage());
            return false;
        }
    }
    
    /**
     * 删除冲突的索引或数据流 - 使用现代 Java API
     * 
     * @param name 索引或数据流名称
     * @return 是否删除成功
     */
    public boolean deleteConflictingIndexOrDataStream(String name) {
        try {
            boolean deleted = false;
            
            // 尝试删除索引
            try {
                DeleteIndexRequest indexRequest = DeleteIndexRequest.of(d -> d.index(name));
                DeleteIndexResponse indexResponse = client.indices().delete(indexRequest);
                if (indexResponse.acknowledged()) {
                    log.info("成功删除冲突的索引: {}", name);
                    deleted = true;
                }
            } catch (Exception e) {
                log.debug("索引不存在: {}", name);
            }
            
            // 尝试删除数据流
            try {
                co.elastic.clients.elasticsearch.indices.DeleteDataStreamRequest dataStreamRequest =
                    co.elastic.clients.elasticsearch.indices.DeleteDataStreamRequest.of(d -> d.name(name));
                co.elastic.clients.elasticsearch.indices.DeleteDataStreamResponse dataStreamResponse = 
                    client.indices().deleteDataStream(dataStreamRequest);
                if (dataStreamResponse.acknowledged()) {
                    log.info("成功删除冲突的数据流: {}", name);
                    deleted = true;
                }
            } catch (Exception e) {
                log.debug("数据流不存在: {}", name);
            }
            
            return deleted;
        } catch (Exception e) {
            log.error("删除冲突的索引或数据流失败: {}", e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * 查询所有ILM策略 - 使用现代 Java API
     * 
     * @return 策略信息的格式化字符串
     */
    public String getAllILMPolicies() {
        try {
            GetLifecycleRequest request = GetLifecycleRequest.of(g -> g);
            GetLifecycleResponse response = client.ilm().getLifecycle(request);
            
            log.info("获取到 {} 个ILM策略", response.result().size());
            
            // 格式化策略信息为字符串
            StringBuilder result = new StringBuilder();
            result.append("ILM策略列表 (共 ").append(response.result().size()).append(" 个):\n\n");
            
            response.result().forEach((policyName, lifecycle) -> {
                result.append("策略名称: ").append(policyName).append("\n");
                result.append("版本: ").append(lifecycle.version()).append("\n");
                result.append("修改时间: ").append(lifecycle.modifiedDate()).append("\n");
                
                var policy = lifecycle.policy();
                if (policy != null) {
                    result.append("阶段配置:\n");
                    
                    var phases = policy.phases();
                    if (phases != null) {
                        var hot = phases.hot();
                        if (hot != null) {
                            result.append("  - 热阶段: ").append(hot.minAge()).append("\n");
                        }
                        var warm = phases.warm();
                        if (warm != null) {
                            result.append("  - 温阶段: ").append(warm.minAge()).append("\n");
                        }
                        var cold = phases.cold();
                        if (cold != null) {
                            result.append("  - 冷阶段: ").append(cold.minAge()).append("\n");
                        }
                        var delete = phases.delete();
                        if (delete != null) {
                            result.append("  - 删除阶段: ").append(delete.minAge()).append("\n");
                        }
                    }
                }
                result.append("\n");
            });
            
            return result.toString();
            
        } catch (Exception e) {
            log.error("获取ILM策略失败: {}", e.getMessage(), e);
            return "获取ILM策略失败: " + e.getMessage();
        }
    }
    
    /**
     * 创建紧急ILM策略 - 使用现代 Java API
     * 当磁盘空间不足时，将温阶段数据立即转为冷阶段
     * 
     * @return 是否创建成功
     */
    public boolean createEmergencyILMPolicy() {
        try {
            // 使用类型安全的构建器创建紧急ILM策略
            PutLifecycleRequest request = PutLifecycleRequest.of(p -> p
                .name("audit_log_emergency_policy")
                .policy(policy -> policy
                    .phases(phases -> phases
                        .hot(Phase.of(hot -> hot
                            .minAge(Time.of(t -> t.time("0ms")))
                            .actions(JsonData.of(Map.of(
                                "rollover", Map.of(
                                    "max_age", "1d",
                                    "max_size", "50gb"
                                ),
                                "set_priority", Map.of("priority", 100)
                            )))
                        ))
                        .cold(Phase.of(cold -> cold
                            .minAge(Time.of(t -> t.time("0ms")))
                            .actions(JsonData.of(Map.of(
                                "set_priority", Map.of("priority", 0),
                                "freeze", Map.of()
                            )))
                        ))
                    )
                )
            );
            
            PutLifecycleResponse response = client.ilm().putLifecycle(request);
            
            log.info("紧急ILM策略创建成功");
            return response.acknowledged();
            
        } catch (Exception e) {
            log.error("创建紧急ILM策略失败: {}", e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * 将紧急策略应用到所有审计日志索引 - 使用现代 Java API
     * 
     * @return 是否应用成功
     */
    public boolean applyEmergencyPolicyToAuditIndices() {
        try {
            // 创建索引模板，应用紧急策略
            Map<String, JsonData> templateSettings = Map.of(
                "index.lifecycle.name", JsonData.of("audit_log_emergency_policy"),
                "index.lifecycle.rollover_alias", JsonData.of("audit-logs")
            );
            
            PutIndexTemplateRequest request = PutIndexTemplateRequest.of(t -> t
                .name("audit_log_emergency_template")
                .indexPatterns("audit-logs-*")
                .template(template -> template
                    .settings(IndexSettings.of(s -> s
                        .otherSettings(templateSettings)
                    ))
                )
                .priority(600) // 高于普通模板的优先级
            );
            
            PutIndexTemplateResponse response = client.indices().putIndexTemplate(request);
            
            log.info("紧急策略应用到审计日志索引成功");
            return response.acknowledged();
            
        } catch (Exception e) {
            log.error("应用紧急策略失败: {}", e.getMessage(), e);
            return false;
        }
    }
    
    
    /**
     * 获取集群磁盘使用情况 - 使用现代 Java API
     * 
     * @return 磁盘使用情况的节点统计信息
     */
    public String getClusterDiskUsage() {
        try {
            co.elastic.clients.elasticsearch.nodes.NodesStatsRequest request = 
                co.elastic.clients.elasticsearch.nodes.NodesStatsRequest.of(n -> n
                    .metric("fs")
                );
            
            co.elastic.clients.elasticsearch.nodes.NodesStatsResponse response = 
                client.nodes().stats(request);
            
            log.info("获取到 {} 个节点的磁盘使用情况", response.nodes().size());
            
            // 返回简化的磁盘信息
            StringBuilder result = new StringBuilder();
            response.nodes().forEach((nodeId, nodeStats) -> {
                result.append("Node: ").append(nodeId).append("\n");
                var fs = nodeStats.fs();
                if (fs != null) {
                    var total = fs.total();
                    if (total != null) {
                        result.append("Total: ").append(total.totalInBytes()).append(" bytes\n");
                        result.append("Available: ").append(total.availableInBytes()).append(" bytes\n");
                    }
                }
            });
            
            return result.toString();
            
        } catch (Exception e) {
            log.error("获取集群磁盘使用情况失败: {}", e.getMessage(), e);
            return "{}";
        }
    }
}
